package com.rizhov.tasks;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

import com.rizhov.datastructures.PriorityQueue;
import com.rizhov.datastructures.PriorityQueue.IHasPriority;
import com.rizhov.graph.Graph;

public class SecondTask implements Task {

    /** connected graph **/
    public Graph graph;

    /** result string contains edges to be built */
    protected String resultString;

    private SecondTask() {

    }

    public SecondTask(Graph graph) {
	this.graph = graph;
	calculateTask();
    }

    @Override
    public Object clone() {
	SecondTask copy = new SecondTask();
	copy.graph = this.graph;
	copy.resultString = this.resultString;
	return copy;
    }

    @Override
    public void setGraph(Graph graph) {
	this.graph = graph;
	calculateTask();
    }

    @Override
    public void restart() {
	calculateTask();
    }

    @Override
    public String getResult() {
	return resultString;
    }

    private static class ColoredVertex implements IHasPriority {

	/** indexes of connected vertexes */
	protected ArrayList<Integer> adj;

	protected double d; // верхняя оценка кратчайшего пути
	protected ColoredVertex p; // ссылка на родительскую вершину
	protected int indexInGraph;

	@Override
	public int getPriority() {
	    return (int) Math.round(d);
	}

    }

    public void calculateTask() {
	StringBuffer sb = new StringBuffer();
	ArrayList<ColoredVertex> resultList = dijkstra(0);
	resultString = sb.toString();
	File resultFile = new File("task2Data.txt");
	try {
	    resultFile.createNewFile();
	    PrintWriter pw = new PrintWriter(resultFile);

	    int indexOfLowestEccentricity = -1;
	    double lowestEccentricity = Double.MAX_VALUE;

	    for (int i = 0; i < graph.getVertexCount(); i++) {
		int indexOfFarestVertex = -1;
		double longestWay = -1; // eccentricity
		resultList = dijkstra(i);
		pw.write("-------------------------------------------------------------- \nVertex #"
			+ i + ", shortest ways to vertexes are:");
		for (int j = 0; j < resultList.size(); j++) {

		    if (resultList.get(j).d != Integer.MAX_VALUE
			    && resultList.get(j).d > longestWay) {
			longestWay = resultList.get(j).d;
			indexOfFarestVertex = resultList.get(j).indexInGraph;
		    }
		    pw.write("\n[index: " + j + ", value:"
			    + graph.getVertexData(resultList.get(j).indexInGraph) + "] = "
			    + resultList.get(j).d);
		    ColoredVertex temp = resultList.get(j);
		    if (temp.p != null) {
			pw.write(", way = ");
			while (temp.p != null) {
			    pw.write(String.valueOf(temp.indexInGraph));
			    temp = temp.p;
			    pw.write(temp.indexInGraph);
			    if (temp.p != null) {
				pw.write("\u2190");
			    } else {
				pw.write(String.valueOf(i));
			    }
			}
		    }
		    pw.write("\n");

		}
		pw.write("Eccentricity = " + longestWay + ", index of farest attainable vertex = "
			+ indexOfFarestVertex + "\n");
		if (longestWay < lowestEccentricity) {
		    for (int j = 0; j < resultList.size(); j++) {
			if (j != 0 && resultList.get(j).p == null) {
			    break;
			}
			if (j == resultList.size() - 1) {
			    lowestEccentricity = longestWay;
			    indexOfLowestEccentricity = i;
			}
		    }
		}
	    }
	    pw.write("-------------------------------------------------------------- \nResult:\n");
	    resultString = "Radius = " + lowestEccentricity + ", center of graph = "
		    + indexOfLowestEccentricity;
	    pw.write("Radius = " + lowestEccentricity + ", center of graph = "
		    + indexOfLowestEccentricity);
	    pw.flush();
	    pw.close();
	} catch (IOException e) {
	    e.printStackTrace();
	}
    }

    private void initializeSingleSource(ArrayList<ColoredVertex> coloredVertexArr, int indexOfS) {

	for (int i = 0; i < graph.getVertexCount(); i++) {
	    ColoredVertex tempColoredVertex = new ColoredVertex();
	    tempColoredVertex.indexInGraph = i;
	    tempColoredVertex.d = Integer.MAX_VALUE;
	    tempColoredVertex.p = null;
	    tempColoredVertex.adj = new ArrayList<Integer>();
	    for (int j = 0; j < graph.getVertexCount(); j++) {
		if (graph.isEdgeExist(i, j)) {
		    tempColoredVertex.adj.add(j);
		}
	    }
	    coloredVertexArr.add(tempColoredVertex);
	}
	coloredVertexArr.get(indexOfS).d = 0;
    }

    private void relax(ColoredVertex u, ColoredVertex v, int weightUV) {
	if (v.d > u.d + weightUV) {
	    v.d = u.d + weightUV;
	    v.p = u;
	}
    }

    private ArrayList<ColoredVertex> dijkstra(int indexOfS) {
	ArrayList<ColoredVertex> coloredVertexArr = new ArrayList<ColoredVertex>();
	initializeSingleSource(coloredVertexArr, indexOfS);
	ArrayList<ColoredVertex> A = new ArrayList<ColoredVertex>();
	PriorityQueue<ColoredVertex> Q = new PriorityQueue<ColoredVertex>();
	for (int i = 0; i < graph.getVertexCount(); i++) {
	    Q.enqueue(coloredVertexArr.get(i));
	}
	while (!Q.isEmpty()) {
	    ColoredVertex u = Q.dequeue();
	    A.add(u);
	    for (int i = 0; i < u.adj.size(); i++) {
		relax(u, coloredVertexArr.get(u.adj.get(i)), Integer.valueOf((String) graph
			.getEdgeWeight(u.indexInGraph,
				coloredVertexArr.get(u.adj.get(i)).indexInGraph)));
	    }
	}
	return A;
    }

}
